﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using System.ComponentModel;

namespace LowLevelGraphics.Filter
{
    //Todo: DualEnergy, test
    /// <summary>
    /// 
    /// </summary>
    public class DualEnergy : BaseImageFilter, ITwoSourceFilter
    {
        /** Mean grey level from the low-energy radiograph of an object of known thickness and composition */
        protected static double di0l = 100.0;

        /** Mean grey level from the high-energy radiograph of an object of known thickness and composition */
        protected static double di0h = 100.0;

        /** Linear attenuation coefficient at low energy of the basis material A */
        protected static double dmual = 0.741;

        /** Linear attenuation coefficient at high energy of the basis material A */
        protected static double dmuah = 0.388;

        /** Linear attenuation coefficient at low energy of the basis material B */
        protected static double dmubl = 0.423;

        /** Linear attenuation coefficient at high energy of the basis material B */
        protected static double dmubh = 0.262;
        
        /** Thickness of the known material to calculate incident x-ray intensity (in cm) */
        protected static double dt = 5.0;

        /** Projection angle in degrees */
        protected static double dangle = 45.0;

        /** Coefficient R for the calculation of the projection matrix */
        protected static double dR = 1.0;

        protected static double drad = 0.0f;

        protected static double[] Image = null;

        /// <summary>
        /// Initializes a new instance of the <see cref="DualEnergy"/> class.
        /// </summary>
        public DualEnergy()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="DualEnergy"/> class.
        /// </summary>
        /// <param name="_dualEnergy">The _dual energy.</param>
        internal DualEnergy(DualEnergy _dualEnergy)
        {
        }

        /// <summary>
        /// Execute this Filter
        /// A Debug Statement is written if a concrete Filter is called
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <returns></returns>
        public override UnsafeBitmap Execute(UnsafeBitmap _bitmap)
        {
            Process((UnsafeBitmap)_bitmap.Clone(), _bitmap);
            return _bitmap;
        }

        #region ITwoSourceFilter Members

        /// <summary>
        /// Processes the specified _bitmap.
        /// </summary>
        /// <param name="_bitmap">The _bitmap.</param>
        /// <param name="bmp">The BMP.</param>
        /// <returns></returns>
        public UnsafeBitmap Process(UnsafeBitmap _bitmap, UnsafeBitmap bmp)
        {
            int iW = _bitmap.Width;
            int iH = _bitmap.Height;
            int iLen = iW * iH;

            /* Calculate the intensity of the incident beam on the flat ROI (the known object) */
            double di0lin = di0l * Math.Exp(dmual * dt);
            double di0hin = di0h * Math.Exp(dmuah * dt);
            dR = dmuah * dmubl - dmubh * dmual;
            double drad = dangle * Math.PI / 180;

            UnsafeBitmap impLE = null;
            UnsafeBitmap impHE = null;

            // Create the hybrid image

            UnsafeBitmap h_ip = new UnsafeBitmap(_bitmap);

            // Start the calculation of the hybrid image at the projection angle 

            object[] h_ipGetPixels = h_ip.GetPixels(new SingleIntValuePixelDelegate(x => x));
            double[] doubleArray = (double[])Array.ConvertAll<object, double>(h_ipGetPixels, Convert.ToDouble);

            Image = doubleArray; // h_ip.GetPixels();
            float[] asImage = new float[iLen];
            double[] asLE = (double[])doubleArray.Clone();//(short[])impLE.getProcessor().getPixels();
            double[] asHE = (double[])doubleArray.Clone(); //(short[])impHE.getProcessor().getPixels();
            double[] ml = new double[iLen];
            double[] mh = new double[iLen];

            int ny = 0;
            int nx = 0;
            int i = 0;

            for (i = 0; i < iLen; i++)
            {
                ml[i] = (Math.Log(di0lin / asLE[i]));
                mh[i] = (Math.Log(di0hin / asHE[i]));
            }

            for (i = 0; i < iLen; i++)
            {
                Image[i] = (float)((dmubl * Math.Cos(drad) - dmual * Math.Sin(drad)) / dR * mh[i] +
                        (dmuah * Math.Sin(drad) - dmubh * Math.Cos(drad)) / dR * ml[i]);
            }

            int nHeight = h_ip.Height;
            int nWidth = h_ip.Width;
            int nIndex = 0;

            for (ny = 0; ny < nHeight; ny++)
            {
                for (nx = 0; nx < nWidth; nx++)
                {
                    h_ip.SetPixel(nx, ny, Color.FromArgb((int)Image[nIndex]));
                    nIndex++;
                }
            }
            h_ip.Dispose();

            return _bitmap;
        }

        #endregion

        /// <summary>
        /// This is used to clone a filter, each filter must implement it's own clone functionality
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new DualEnergy(this);
        }
    }
}
